#pragma once

#include <cstdint>
#include <memory>

#include "EventLoop.hpp"
#include "Socket.hpp"
#include "Channel.hpp"
#include "Buffer.hpp"
#include "Any.hpp"

typedef enum
{
    DISCONNECTED, // 连接关闭
    CONNECTING,   // 连接成功，待处理
    CONNECTED,    // 连接成功，可以通信
    DISCONNECTING // 待关闭
} ConnStatu;

class Connection;

using PtrConnection = std::shared_ptr<Connection>;
// 该继承用作，内部获取this时以shared_ptr形式获取
class Connection : public std::enable_shared_from_this<Connection>
{
private:
    uint64_t _connId;            // 连接id（同为定时器id）
    int _sockFd;                 // 该连接对应的文件描述符
    bool _enableInactiveRelease; // 是否设为非活跃销毁的标志（默认false）
    EventLoop *_loop;            // 连接所关联的一个EventLoop
    ConnStatu _statu;            // 连接状态
    Socket _socket;              // socket管理
    Channel _channel;            // 事件管理
    Buffer _inBuffer;            // 输入缓冲区
    Buffer _outBuffer;           // 输出缓冲区
    Any _context;                // 请求的接收处理上下文

    using ConnectedCallback = std::function<void(const PtrConnection &)>;
    using MessageCallback = std::function<void(const PtrConnection &, Buffer *)>;
    using ClosedCallback = std::function<void(const PtrConnection &)>;
    using AnyEventCallback = std::function<void(const PtrConnection &)>;
    ConnectedCallback _connectCallback;
    MessageCallback _messageCallback;
    ClosedCallback _closedCallback;
    AnyEventCallback _anyEventCallback;
    /*就应该从管理(sever模块)的地方移除掉自己的信息*/
    ClosedCallback _serverClosedCallback;

private: // 内部接口
    /*五个Channel事件回调函数*/

    // 连接可读
    void HandleRead()
    {
        // 1、接收
        char buffer[65535];
        ssize_t retLen = _socket.NonBlockRecv(buffer, sizeof(buffer));
        if (retLen < 0)
        {
            // 不能直接关闭，关闭前要收尾工作，如缓冲区内剩余数据处理
            return ShutDownInLoop();
        }
        // 2、放入接收（输入）缓存区
        _inBuffer.WriteAndPush(buffer, retLen);

        if (_inBuffer.ReadAbleSize() > 0)
        {
            // 调用读事件
            return _messageCallback(shared_from_this(), &_inBuffer);
        }
    }

    // 连接可写
    void HandleWrite()
    {
        // 1、发送
        ssize_t ret = _socket.NonBlockSend(_outBuffer.ReadPosition(), _outBuffer.ReadAbleSize());
        // 发送错误 -》 关闭连接
        if (ret < 0)
        {
            // 查看“输入缓冲区”是否有剩余数据未读/未处理
            if (_outBuffer.ReadAbleSize() > 0)
            {
                _messageCallback(shared_from_this(), &_inBuffer);
            }
            return Release();
        }

        // 2、更新输出缓存区，读位置
        _outBuffer.MoveReadOffset(ret);
        // 3、判断“输出缓存区”，是否还有数据需要发送
        if (_outBuffer.ReadAbleSize() == 0)
        {
            // 关闭该连接的输出事件
            _channel.DisableWrite();
            // 若为待关闭状态，则直接关闭
            if (_statu == DISCONNECTING)
            {
                _statu = DISCONNECTED;
                return Release();
            }
        }
    }

    // 关闭(释放连接)
    void HandleClose()
    {
        // 若连接断开，则通信就无法继续及对应的事件回调也不会触发，只用考虑接收缓存区是否还有未处理数据即可
        if (_inBuffer.ReadAbleSize() > 0)
        {
            _messageCallback(shared_from_this(), &_inBuffer);
        }
        return Release();
    }

    // 错误(当前默认的直接关闭连接)
    void HandleError()
    {
        return HandleClose();
    }

    // 任意事件
    // 1、_enableInactiveRelease非活跃标志位设置为true，则刷新
    // 2、回调组件使用者设置的_anyEventCallback
    void HandleEvent()
    {
        if (_enableInactiveRelease)
            _loop->TimerRefresh(_connId);
        if (_anyEventCallback)
            _anyEventCallback(shared_from_this());
    }

private: /*通过连接执行对应的任务（通过loop模块执行）*/
    // 连接成功后的设置
    void EstablishedInLoop()
    {
        // 1、修改状态(连接成功可通信前的状态，一定为连接成功，待处理)
        assert(_statu == CONNECTING);
        _statu = CONNECTED;

        // 2、启动该连  接的读事件监控
        //(这步设定不能放在构造中，若放在构造中直接启动读监控，可能会使读事件调用后，非活跃连接销毁的刷新(任意事件就会触发刷新)直接调用，而此时刷新还未设置)
        _channel.EnableRead();

        // 3、调用连接成功的回调函数
        if (_connectCallback)
            _connectCallback(shared_from_this());
    }

    // 释放连接（不考虑buffer内残余数据直接释放连接）
    void ReleaseInLoop()
    {

        // 1、修改连接状态
        // 完全释放前，需要保证残余数据处理完毕
        
        _statu = DISCONNECTED;

        // 2、移除连接的事件监控
        _channel.Remove();

        // 3、关闭连接（文件描述符）
        _socket.Close();

        // 4、取消连接内的定时销毁任务
        _loop->TimerCancel(_connId);

        // 5、调用使用者的关闭回调函数
        if (_closedCallback)
            _closedCallback(shared_from_this());

        // 6、从服务模块中移除该连接
        if (_serverClosedCallback)
            _serverClosedCallback(shared_from_this());
    }

    // 这个接口并不是实际的发送接口，而只是把数据放到了发送缓冲区，启动了可写事件监控
    void SendInLoop(Buffer &buf)
    {

        if (_statu == DISCONNECTED)
            return;
        _outBuffer.WriteBufferAndPush(buf);
        if (_channel.WriteAble() == false)
        {
            _channel.EnableWrite();
        }
    }

    // 关闭连接（只处理残余数据,连接还未关闭）
    void ShutDownInLoop()
    {
        // 1、修改连接状态
        _statu = DISCONNECTING;

        // 2、处理输入缓存区内的残余数据
        if (_inBuffer.ReadAbleSize() > 0)
        {
            _messageCallback(shared_from_this(), &_inBuffer);
        }

        // 3、处理输出缓存区内的残余数据（发送缓存区可读，数据未发送完毕）
        if (_outBuffer.ReadAbleSize() > 0)
        {
            if (_channel.WriteAble() == false)
            {
                _channel.EnableWrite();
            }
        }
        if (_outBuffer.ReadAbleSize() == 0)
        {
            Release();
        }
    }

    // 启动非活跃销毁（连接有生命周期）
    void EnableInactiveReleaseInLoop(int sec)
    {
        // 1. 将判断标志 _enable_inactive_release 置为true
        _enableInactiveRelease = true;
        // 2. 如果当前定时销毁任务已经存在，那就刷新延迟一下即可
        if (_loop->HasTimer(_connId))
        {
            return _loop->TimerRefresh(_connId);
        }
        // 3. 如果不存在定时销毁任务，则新增
        _loop->TimerAdd(_connId, sec, std::bind(&Connection::Release, this));
    }

    // 取消非活跃销毁（连接有生命周期）
    void CancelInactiveReleaseInLoop()
    {
        _enableInactiveRelease = false;
        if (_loop->HasTimer(_connId))
        {
            _loop->TimerCancel(_connId);
        }
    }

    // 协议切换（回调函数，正文修改）
    void UpgradeInLoop(const Any &context,
                       const ConnectedCallback &conn,
                       const MessageCallback &msg,
                       const ClosedCallback &closed,
                       const AnyEventCallback &event)
    {
        _context = context;
        _connectCallback = conn;
        _messageCallback = msg;
        _closedCallback = closed;
        _anyEventCallback = event;
    }

public: // 外部接口
    Connection(EventLoop *loop, uint64_t conn_id, int sockfd) : _connId(conn_id), _sockFd(sockfd),
                                                                _enableInactiveRelease(false), _loop(loop), _statu(CONNECTING), _socket(_sockFd),
                                                                _channel(loop, _sockFd)
    {
        _channel.SetCloseCallback(std::bind(&Connection::HandleClose, this));
        _channel.SetEventCallback(std::bind(&Connection::HandleEvent, this));
        _channel.SetReadCallback(std::bind(&Connection::HandleRead, this));
        _channel.SetWriteCallback(std::bind(&Connection::HandleWrite, this));
        _channel.SetErrorCallback(std::bind(&Connection::HandleError, this));
    }
    ~Connection() { DLOG("RELEASE CONNECTION:%p", this); }
    // 获取管理的文件描述符
    int Fd() { return _sockFd; }
    // 获取连接ID
    int Id() { return _connId; }
    // 是否处于CONNECTED状态
    bool Connected() { return (_statu == CONNECTED); }
    // 设置上下文--连接建立完成时进行调用
    void SetContext(const Any &context) { _context = context; }
    // 获取上下文，返回的是指针
    Any *GetContext() { return &_context; }
    void SetConnectedCallback(const ConnectedCallback &cb) { _connectCallback = cb; }
    void SetMessageCallback(const MessageCallback &cb) { _messageCallback = cb; }
    void SetClosedCallback(const ClosedCallback &cb) { _closedCallback = cb; }
    void SetAnyEventCallback(const AnyEventCallback &cb) { _anyEventCallback = cb; }
    void SetSrvClosedCallback(const ClosedCallback &cb) { _serverClosedCallback = cb; }

    // 连接建立就绪后，进行channel回调设置，启动读监控，调用_connected_callback
    void Established()
    {
        _loop->RunInLoop(std::bind(&Connection::EstablishedInLoop, this));
    }

    // 发送数据，将数据放到发送缓冲区，启动写事件监控
    void Send(const char *data, size_t len)
    {
        // 外界传入的data，可能是个临时的空间，我们现在只是把发送操作压入了任务池，有可能并没有被立即执行
        // 因此有可能执行的时候，data指向的空间有可能已经被释放了。
        Buffer buf;
        buf.WriteAndPush(data, len);
        _loop->RunInLoop(std::bind(&Connection::SendInLoop, this, std::move(buf)));
    }

    // 提供给组件使用者的关闭接口--并不实际关闭，需要判断有没有数据待处理
    void Shutdown()
    {
        _loop->RunInLoop(std::bind(&Connection::ShutDownInLoop, this));
    }

    //
    void Release()
    {
        _loop->QueueInLoop(std::bind(&Connection::ReleaseInLoop, this));
    }

    // 启动非活跃销毁，并定义多长时间无通信就是非活跃，添加定时任务
    void EnableInactiveRelease(int sec)
    {
        _loop->RunInLoop(std::bind(&Connection::EnableInactiveReleaseInLoop, this, sec));
    }

    // 取消非活跃销毁
    void CancelInactiveRelease()
    {
        _loop->RunInLoop(std::bind(&Connection::CancelInactiveReleaseInLoop, this));
    }

    // 切换协议---重置上下文以及阶段性回调处理函数 -- 而是这个接口必须在EventLoop线程中立即执行
    // 防备新的事件触发后，处理的时候，切换任务还没有被执行--会导致数据使用原协议处理了。
    void Upgrade(const Any &context, const ConnectedCallback &conn, const MessageCallback &msg,
                 const ClosedCallback &closed, const AnyEventCallback &event)
    {
        _loop->AssertInLoop();
        _loop->RunInLoop(std::bind(&Connection::UpgradeInLoop, this, context, conn, msg, closed, event));
    }
};